package com.originskyseed.platform.count.service.impl;

import java.time.Instant;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.Date;
import java.util.List;

import javax.servlet.http.HttpServletRequest;

import org.apache.commons.lang3.StringUtils;
import org.apache.dubbo.config.annotation.DubboService;
import org.springframework.data.mongodb.core.query.Criteria;
import org.springframework.data.mongodb.core.query.Query;
import org.springframework.data.mongodb.core.query.Update;
import org.springframework.stereotype.Component;

import com.alibaba.fastjson.JSONObject;
import com.originskyseed.platform.core.base.BasePage;
import com.originskyseed.platform.core.mongodb.api.MongoUtil;
import com.originskyseed.platform.core.redis.util.RedisUtil;
import com.originskyseed.platform.core.tenant.util.TenantCodeUtil;
import com.originskyseed.platform.core.util.bean.PrimaryKeyGeneratorUtil;
import com.originskyseed.platform.count.model.Online;
import com.originskyseed.platform.count.service.IOnlineService;
import com.originskyseed.platform.user.model.UserInfo;

/**
 * @author tao
 * @date 2020/8/3 16:17
 */

@DubboService
@Component("onlineService")
public class OnlineServiceImpl implements IOnlineService {

	private static final String TABLENAME = "online_user";

	private static final int PAGE_SIZE = 200;

	@Override
	public boolean online(HttpServletRequest request) throws Exception {
		boolean result = false;
		String sessionId = request.getSession().getId();
		// 确认是否用户登录
		Object value = RedisUtil.getInstance().getForMap(sessionId, "USER_INFO");
		if (value != null) {
			UserInfo userInfo = JSONObject.parseObject(value.toString(), UserInfo.class);
			// 以userId和登录来源区分，查询用户当天时长信息
			Criteria criteria = Criteria.where("userId").is(userInfo.getId());
			// criteria.and("source").is(userInfo.getSource());
			criteria.and("createTime").gte(startTime()).lte(endTime());
			List<JSONObject> dataList = MongoUtil.getInstance().selectListByQuery(Query.query(criteria), JSONObject.class, TABLENAME);
			// 当天已登录，时间累加
			if (dataList != null && dataList.size() > 0) {
				// 更新时长信息
				updateOnline(sessionId, dataList);
			} else {
				// 当天第一次登录，新增一条记录
				insertOnline(sessionId, userInfo);
			}
			result = true;
		}
		return result;
	}

	@Override
	public void updateOnline(String tenantCode) throws Exception {
		if (StringUtils.isNotEmpty(tenantCode)) {
			TenantCodeUtil.getInstance().createServerTenantCode(tenantCode);
		}
		// 查询当天的用户时长信息
		Criteria criteria = Criteria.where("createTime").gte(startTime()).lte(endTime());
		int totalRecord = Math.toIntExact(MongoUtil.getInstance().selectCountByQuery(Query.query(criteria), TABLENAME));
		int totalPage = totalRecord % PAGE_SIZE == 0 ? totalRecord / PAGE_SIZE : totalRecord / PAGE_SIZE + 1;
		for (int currentPage = 0; currentPage < totalPage; currentPage++) {
			// 循环遍历判断是否离线
			BasePage<JSONObject> page = new BasePage<>(currentPage, PAGE_SIZE);
			List<JSONObject> resultList = MongoUtil.getInstance().selectListByPage(Query.query(criteria), page, JSONObject.class, TABLENAME);
			List<String> redisKeys = new ArrayList<>(resultList.size());
			resultList.forEach(jsonObject -> {
				redisKeys.add(jsonObject.getString("sessionId"));
			});
			for (String key : redisKeys) {
				boolean hasKey = RedisUtil.getInstance().hasKey(key);
				if (!hasKey) {
					// 表明已退出登录
					for (JSONObject jsonObject : resultList) {
						if (jsonObject.getString("sessionId").equals(key)) {
							logout(jsonObject);
							break;
						}
					}
				}
			}
		}
	}

	@Override
	public boolean offline(String sessionId) throws Exception {
		List<JSONObject> dataList = MongoUtil.getInstance().selectListByQuery(Query.query(Criteria.where("sessionId").is(sessionId)), JSONObject.class, TABLENAME);
		if (dataList != null && dataList.size() > 0) {
			logout(dataList.get(0));
			return RedisUtil.getInstance().delete(sessionId);
		}
		return Boolean.FALSE;
	}

	/**
	 * 下线时长统计
	 *
	 * @param jsonObject
	 */
	private void logout(JSONObject jsonObject) {
		Online online = JSONObject.toJavaObject(jsonObject, Online.class);
		long seconds = (Instant.now().toEpochMilli() - online.getUpdateTime().getTime()) / 1000;
		long totalTime = timeToSecond(online.getTotalTime()) + seconds;
		Update update = new Update();
		update.set("sessionId", null);
		update.set("status", 0);
		// 在线时长置为0
		update.set("onlineTime", "00:00:00");
		// 总时长累计
		update.set("totalTime", secondToTime(totalTime));
		update.set("loginTime", null);
		update.set("logoutTime", new Date());
		update.set("updateTime", new Date());
		update.set("version", online.getVersion() + 1);
		Criteria criteria = Criteria.where("_id").is(jsonObject.getString("_id")).and("version").is(online.getVersion());
		MongoUtil.getInstance().update(Query.query(criteria), update, TABLENAME);
	}

	/**
	 * 更新时长信息
	 *
	 * @param dataList 时长信息
	 */
	private void updateOnline(String sessionId, List<JSONObject> dataList) {
		Online online = JSONObject.toJavaObject(dataList.get(0), Online.class);
		// 当前在线时长增加数 = 当前时间 - 上次刷新时间（上一次sessionId和本次sessionId不一致，说明已离线，重新计数，部分时长信息会丢失）
		long seconds = 0;
		if (online != null && online.getUpdateTime() != null) {
			seconds = (Instant.now().toEpochMilli() - online.getUpdateTime().getTime()) / 1000;
		}
		long onlineTime = StringUtils.equals(sessionId, online.getSessionId()) ? timeToSecond(online.getOnlineTime()) + seconds : seconds;
		// 当天累计时长增加
		long totalTime = timeToSecond(online.getTotalTime()) + seconds;
		// 更新时长信息
		Update update = new Update();
		// 更新当前的sessionid
		update.set("sessionId", sessionId);
		update.set("updateTime", new Date());
		update.set("onlineTime", secondToTime(onlineTime));
		update.set("totalTime", secondToTime(totalTime));
		// 设置为登录状态
		update.set("status", 1);
		update.set("logoutTime", null);
		update.set("version", online.getVersion() + 1);
		Criteria updateCriteria = Criteria.where("_id").is(dataList.get(0).getString("_id")).and("version").is(online.getVersion());
		MongoUtil.getInstance().update(Query.query(updateCriteria), update, TABLENAME);
	}

	/**
	 * 新增时长信息
	 *
	 * @param sessionId sessionId
	 * @param userInfo  用户信息
	 */
	private void insertOnline(String sessionId, UserInfo userInfo) {
		Online online = new Online();
		online.setId(PrimaryKeyGeneratorUtil.getInstance().getRandomKey());
		online.setSessionId(sessionId);
		online.setIp(userInfo.getFinallyLoginIp());
		online.setUserName(userInfo.getName());
		online.setUserId(userInfo.getId());
		online.setAccount(userInfo.getAccount());
		online.setStatus(1);
		online.setVersion(1);
		// online.setSource(userInfo.getSource());
		if (userInfo.getFinallyLoginTime() != null) {
			online.setOnlineTime(secondToTime((Instant.now().toEpochMilli() - userInfo.getFinallyLoginTime().getTime()) / 1000));
		}
		online.setTotalTime(online.getOnlineTime());
		online.setLoginTime(userInfo.getFinallyLoginTime());
		online.setCreateTime(new Date());
		online.setUpdateTime(online.getLoginTime());
		MongoUtil.getInstance().insert(online, TABLENAME);
	}

	/**
	 * 获取当天的开始时间 00:00:00
	 *
	 * @return
	 */
	private static Date startTime() {
		Calendar startTime = Calendar.getInstance();
		startTime.set(Calendar.HOUR_OF_DAY, 0);
		startTime.set(Calendar.MINUTE, 0);
		startTime.set(Calendar.SECOND, 0);
		startTime.set(Calendar.MILLISECOND, 0);
		return startTime.getTime();
	}

	/**
	 * 获取当天的结束时间 23:59:59.999
	 *
	 * @return
	 */
	private static Date endTime() {
		Calendar endTime = Calendar.getInstance();
		endTime.set(Calendar.HOUR_OF_DAY, 23);
		endTime.set(Calendar.MINUTE, 59);
		endTime.set(Calendar.SECOND, 59);
		endTime.set(Calendar.MILLISECOND, 999);
		return endTime.getTime();
	}

	/**
	 * 秒 转换为 天：时：分：秒
	 *
	 * @param second
	 * @return
	 */
	private static String secondToTime(long second) {
		// 转换天数
		long days = second / 86400;
		// 剩余秒数
		second = second % 86400;
		// 转换小时数
		long hours = second / 3600;
		// 剩余秒数
		second = second % 3600;
		// 转换分钟
		long minutes = second / 60;
		// 剩余秒数
		second = second % 60;
		if (0 < days) {
			return days + ":" + isNeedZero(String.valueOf(hours)) + ":" + isNeedZero(String.valueOf(minutes)) + ":" + isNeedZero(String.valueOf(second));
		} else {
			return isNeedZero(String.valueOf(hours)) + ":" + isNeedZero(String.valueOf(minutes)) + ":" + isNeedZero(String.valueOf(second));
		}
	}

	/**
	 * 天：时：分：秒 转换为 秒
	 *
	 * @param time
	 * @return
	 */
	private static long timeToSecond(String time) {
		long hours = 0, mins = 0, seconds = 0;
		if (StringUtils.isNotBlank(time)) {
			String[] times = time.split(":");
			int length = times.length;
			// 数组长度为4，说明含有天数
			if (length == 4) {
				hours = (Long.parseLong(times[length - 4]) * 24 + Long.parseLong(times[length - 3])) * 3600;
			} else {
				hours = Long.parseLong(times[length - 3]) * 3600;
			}
			mins = (Long.parseLong(times[length - 2])) * 60;
			seconds = Long.parseLong(times[length - 1]);
		}
		return hours + mins + seconds;
	}

	/**
	 * 确认是否补全
	 *
	 * @param content
	 * @return
	 */
	private static String isNeedZero(String content) {
		return content.length() > 1 ? content : "0" + content;
	}
}
